package org.ssi.web.result;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

/**
 * 功能概述：<br>
 * 在内存中动态构建XML结点,通过实现buildXmlContent()方法<br>
 * 
 * <pre>
 * public void buildXmlContent() {
 * 	//添加一个名称为&quot;chart&quot;的新结点，并将其设置为当前操作的结点
 * 	this.setChildAsCurrent(&quot;chart&quot;);
 * 	this.setAttribute(&quot;id&quot;, &quot;13&quot;);
 * 
 * 	this.setChildAsCurrent(&quot;location&quot;);
 * 	//添加一个子结点
 * 	this.addTxtChild(&quot;width&quot;, &quot;13&quot;);
 * 	this.addTxtChild(&quot;height&quot;, &quot;45&quot;);
 * 
 * 	this.moveCurrentToParent();
 * 	this.setChildAsCurrent(&quot;style&quot;);
 * 	this.setAttribute(&quot;font&quot;, &quot;宋体&quot;);
 * 
 * }
 * </pre>
 * 
 * @author 王国东
 */
public abstract class AbstXmlBuilder implements Serializable {
	/** 文档对象 */
	private Document document = null;

	/** 当前结点,抽象方法buildXmlContent()的各种操作均基于当前结点 */
	private Node currentNode;

	/**
	 * 初始化工具类，构建DOM树
	 */
	public AbstXmlBuilder() {
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder;
		try {
			builder = builderFactory.newDocumentBuilder();
			this.document = builder.newDocument();
			this.currentNode = this.document;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 创建XML内容
	 */
	public abstract void buildXmlContent();

	/**
	 * 将当前对象中的DOM树写入输出流
	 * 
	 * @param charset
	 *            编码用字符集
	 * @param out
	 *            目标输出流
	 */
	public void writeXmlContent(Charset charset, OutputStream out) {
		buildXmlContent();
		domToStream(this.document, charset, out);
	}

	/**
	 * 将指定的DOM结rootNode使用字符集charsest写入到输出流out中
	 * 
	 * @param rootNode
	 *            元素根结点
	 * @param charset
	 *            字符集
	 * @param out
	 *            目标输出流
	 */
	public static final void domToStream(Node rootNode, Charset charset,
			OutputStream out) {
		try {
			// DOM输入源
			DOMSource doms = new DOMSource(rootNode);
			// DOM输出转化
			StreamResult sr = new StreamResult(out);
			// 转化DOM的工厂
			TransformerFactory tf = TransformerFactory.newInstance();

			Transformer t = tf.newTransformer();
			t.setOutputProperty(OutputKeys.ENCODING, charset.name());
			t.transform(doms, sr);
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 取得当前对象DOM树以指定字符集解码取得XML字符串
	 * 
	 * @return XML字符串
	 */
	public final String getXmlContent(Charset charset) {
		buildXmlContent();
		return domToString(this.document, charset);
	}

	/**
	 * 将DOM树转化XML字符串
	 * 
	 * @param rootNode
	 *            元素根结点
	 * @param charset
	 *            字符集
	 * @return
	 */
	public static final String domToString(Node rootNode, Charset charset) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		domToStream(rootNode, charset, out);

		// 从流中取出转化后的XML文本
		String xmlCnt = "";
		try {
			xmlCnt = out.toString(charset.name());
			return xmlCnt;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return "";
		}
	}

	/**
	 * 使用nodeName为元素名称，添加新元素结点，并将元素结
	 * 
	 * @param nodeName
	 *            新加的元素结点名称
	 */
	protected void setChildAsCurrent(String nodeName) {
		Element node = document.createElement(nodeName);
		this.currentNode.appendChild(node);
		this.currentNode = node;
	}

	/**
	 * 向当前结点添加子结点
	 * 
	 * @param nodeName
	 *            添加的子结点名称
	 */
	protected void addChildNode(String nodeName) {
		Element node = document.createElement(nodeName);
		this.currentNode.appendChild(node);
	}

	/**
	 * 向当前结点设置属性
	 * 
	 * @param name
	 *            属性名称
	 * @param value
	 *            属生值
	 */
	protected void setAttribute(String name, String value) {
		if (this.currentNode.getNodeType() == Node.ELEMENT_NODE) {
			((Element) this.currentNode).setAttribute(name, value);
		}
	}

	/**
	 * 添加文本子结点
	 * 
	 * @param nodeName
	 *            结点名称
	 * @param txt
	 *            文本
	 */
	protected void addTxtChild(String nodeName, String txt) {
		Element parent = document.createElement(nodeName);
		Text txtNode = document.createTextNode(txt);
		parent.appendChild(txtNode);
		this.currentNode.appendChild(parent);
	}

	/**
	 * 向当前元素添加CDATA子结点
	 * 
	 * @param content
	 *            文本内容
	 */
	protected void appendCDATASection(String content) {
		CDATASection section = this.document.createCDATASection(content);
		this.currentNode.appendChild(section);
	}

	/**
	 * 设置当前结点的父结点设为新的当前结点
	 */
	protected void moveCurrentToParent() {
		Node parent = this.currentNode.getParentNode();
		if (parent != null) {
			this.currentNode = parent;
		}
	}
}
